import { types } from "./types";

export class Helper {
  public static CopyFrom4(data: number[], num: number, offset: number) {
    data[offset] = num & 0xff;
    data[offset + 1] = (num >> 8) & 0xff;
    data[offset + 2] = (num >> 16) & 0xff;
    data[offset + 3] = (num >> 24) & 0xff;
  }
  public static CopyFrom4Reverse(data: number[], num: number, offset: number) {
    for (let i = 0; i < 4; i++) data[offset + i] = (num >> ((i ^ 3) << 3)) & 0xff;
  }
  public static CopyFrom8Reverse(data: number[], num: bigint, offset: number) {
    for (let i = 0; i < 8; i++) data[offset + i] = Number((num >> BigInt((i ^ 7) << 3)) & BigInt(0xff));
  }
  public static ToHexString(data: number[], offset: number, length: number): string {
    let text = "";
    for (let i = offset; i < length; i++) {
      if (data[i] < 0x10) text += '0';
      text += data[i].toString(16);
    }
    return text;
  }
  public static ToHex(data: number[]): string {
    return this.ToHexString(data, 0, data.length);
  }
  public static ToBytes(hexstring: string): number[] {
    var buf = types.number_array(hexstring.length >> 1);
    for (var i = 0; i < buf.length; i++) buf[i] = parseInt("0x" + hexstring.substring(i << 1, (i << 1) + 2));
    return buf;
  }
  public static String2Bin(text: string): number[] {
    let byteArray = [];
    for (let i = 0; i < text.length; i++) {
      let code = text.charCodeAt(i);
      if (code <= 0x7F) {
        byteArray.push(code);
      } else if (code <= 0x7FF) {
        byteArray.push(0xC0 | ((code >> 6) & 0x1F));
        byteArray.push(0x80 | (code & 0x3F));
      } else if (code <= 0xFFFF) {
        byteArray.push(0xE0 | ((code >> 12) & 0x0F));
        byteArray.push(0x80 | ((code >> 6) & 0x3F));
        byteArray.push(0x80 | (code & 0x3F));
      } else if (code <= 0x10FFFF) {
        byteArray.push(0xF0 | ((code >> 18) & 0x07));
        byteArray.push(0x80 | ((code >> 12) & 0x3F));
        byteArray.push(0x80 | ((code >> 6) & 0x3F));
        byteArray.push(0x80 | (code & 0x3F));
      }
    }
    return byteArray;
  }
  public static Bin2Ascii(data: number[], pos: number, length: number): string {
    let text = "";
    for (let i = 0; i < length; i++) text += String.fromCharCode(data[pos + i]);
    return text;
  }
  public static Bin2Utf8(bytes: number[], pos: number, length: number): string {
    let str = '';
    let i = pos;
    while (i < length) {
      const byte1 = bytes[i++];
      if (byte1 < 0x80) { // 1字节字符
        str += String.fromCharCode(byte1);
      } else if (byte1 < 0xE0) { // 2字节字符
        const byte2 = bytes[i++];
        str += String.fromCharCode(((byte1 & 0x1F) << 6) | (byte2 & 0x3F));
      } else if (byte1 < 0xF0) { // 3字节字符
        const byte2 = bytes[i++];
        const byte3 = bytes[i++];
        str += String.fromCharCode(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F));
      } else { // 4字节字符（如Emoji）
        const byte2 = bytes[i++];
        const byte3 = bytes[i++];
        const byte4 = bytes[i++];
        console.log(byte1);
        const codePoint = ((byte1 & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
        str += String.fromCodePoint(codePoint);
      }
    }
    return str;
  }
  public static ROLT(data: number, count: number) {
    data = data >>> 0;
    return Helper.uint((data << count) | (data >>> (32 - count)));
  }
  public static byte2uint(data: number[], offset: number) {
    let rlt = 0;
    for (let i = 0; i < 4; i++) if (data[offset + i] != undefined) rlt += data[offset + i] << ((3 - i) << 3);
    return Helper.uint(rlt);
  }
  public static byte2ulong(data: number[], offset: number) {
    return types.ulong(this.byte2uint(data,0), this.byte2uint(data, 4));
  }
  public static uint2byte(data: number) {
    return [(data >> 24) & 0xff, (data >> 16) & 0xff, (data >> 8) & 0xff, data & 0xff];
  }
  public static ArrayCopy(sourceArray: Array<any>, distArrary: Array<any>, length: number) {
    for (length--; length >= 0; length--) distArrary[length] = sourceArray[length];
  }
  public static ArrayCopy2(sourceArray: Array<any>, pos: number, distArrary: Array<any>, pos2: number, length: number) {
    for (length--; length >= 0; length--) distArrary[pos2 + length] = sourceArray[pos + length];
  }
  public static uint(num: number): number {
    return (num & 0xffffffff) >>> 0;
  }
  public static ulong(num: bigint): bigint {
    return num & types.ulong(0xffffffff, 0xffffffff);
  }
  public static ulong2byte(num: bigint): number[] {
    return [Number((num >> BigInt(0)) & BigInt(0xff)), Number((num >> BigInt(8)) & BigInt(0xff)), Number((num >> BigInt(16)) & BigInt(0xff)), Number((num >> BigInt(24)) & BigInt(0xff)), Number((num >> BigInt(32)) & BigInt(0xff)), Number((num >> BigInt(40)) & BigInt(0xff)), Number((num >> BigInt(48)) & BigInt(0xff)), Number((num >> BigInt(56)) & BigInt(0xff))];
  }
  public static CopyFrom8(data: number[], num: bigint, offset: number, count: number) {
    Helper.ArrayCopy2(this.ulong2byte(num), 0, data, offset, count);
  }
  public static CopyFrom88(data: number[], data_offset: number, array: bigint[][], offest: number, length: number, array_length: number) {
    let i, j = offest;
    for (i = data_offset; i < length >> 3; i++, j += 8) {
      Helper.CopyFrom8(data, array[(i / array_length) >> 0][i % array_length], j, 8);
    }
    if ((length & 0b111) != 0) {
      Helper.CopyFrom8(data, array[(i / array_length) >> 0][i % array_length], j, (length & 0b111));
    }
  }
}